#include <cmath>
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>

#include <algorithm>
#include <iostream>

//#include "Application.h"
#include "Camera.h"
//#include "Utils.h"

#include "Mathutil.h"
#include "Quaternion.h"
#include "Matrix33.h"
#include "AABB.h"

using namespace gti;

#define max(a,b) ( (a) > (b) ? (a) : (b) )
#define min(a,b) ( (a) < (b) ? (a) : (b) )

const
int indexlist[64][7] =
{
    {-1,-1,-1,-1,-1,-1,   0}, // 0 inside
    { 0, 4, 7, 3,-1,-1,   4}, // 1 left
    { 1, 2, 6, 5,-1,-1,   4}, // 2 right
    {-1,-1,-1,-1,-1,-1,   0}, // 3 -
    { 0, 1, 5, 4,-1,-1,   4}, // 4 bottom
    { 0, 1, 5, 4, 7, 3,   6}, // 5 bottom, left
    { 0, 1, 2, 6, 5, 4,   6}, // 6 bottom, right
    {-1,-1,-1,-1,-1,-1,   0}, // 7 -
    { 2, 3, 7, 6,-1,-1,   4}, // 8 top
    { 0, 4, 7, 6, 2, 3,   6}, // 9 top, left
    { 1, 2, 3, 7, 6, 5,   6}, //10 top, right
    {-1,-1,-1,-1,-1,-1,   0}, //11 -
    {-1,-1,-1,-1,-1,-1,   0}, //12 -
    {-1,-1,-1,-1,-1,-1,   0}, //13 -
    {-1,-1,-1,-1,-1,-1,   0}, //14 -
    {-1,-1,-1,-1,-1,-1,   0}, //15 -
    { 0, 3, 2, 1,-1,-1,   4}, //16 front
    { 0, 4, 7, 3, 2, 1,   6}, //17 front, left
    { 0, 3, 2, 6, 5, 1,   6}, //18 front, right
    {-1,-1,-1,-1,-1,-1,   0}, //19 -
    { 0, 3, 2, 1, 5, 4,   6}, //20 front, bottom
    { 1, 5, 4, 7, 3, 2,   6}, //21 front, bottom, left
    { 0, 3, 2, 6, 5, 4,   6}, //22 front, bottom, right
    {-1,-1,-1,-1,-1,-1,   0}, //23 -
    { 0, 3, 7, 6, 2, 1,   6}, //24 front, top
    { 0, 4, 7, 6, 2, 1,   6}, //25 front, top, left
    { 0, 3, 7, 6, 5, 1,   6}, //26 front, top, right
    {-1,-1,-1,-1,-1,-1,   0}, //27 -
    {-1,-1,-1,-1,-1,-1,   0}, //28 -
    {-1,-1,-1,-1,-1,-1,   0}, //29 -
    {-1,-1,-1,-1,-1,-1,   0}, //30 -
    {-1,-1,-1,-1,-1,-1,   0}, //31 -
    { 4, 5, 6, 7,-1,-1,   4}, //32 back
    { 0, 4, 5, 6, 7, 3,   6}, //33 back, left
    { 1, 2, 6, 7, 4, 5,   6}, //34 back, right
    {-1,-1,-1,-1,-1,-1,   0}, //35 -
    { 0, 1, 5, 6, 7, 4,   6}, //36 back, bottom
    { 0, 1, 5, 6, 7, 3,   6}, //37 back, bottom, left
    { 0, 1, 2, 6, 7, 4,   6}, //38 back, bottom, right
    {-1,-1,-1,-1,-1,-1,   0}, //39 -
    { 2, 3, 7, 4, 5, 6,   6}, //40 back, top
    { 0, 4, 5, 6, 2, 3,   6}, //41 back, top, left
    { 1, 2, 3, 7, 4, 5,   6}, //42 back, top, right
    {-1,-1,-1,-1,-1,-1,   0}, //43 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //44 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //45 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //46 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //47 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //48 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //49 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //50 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //51 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //52 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //53 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //54 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //55 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //56 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //57 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //58 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //59 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //60 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //61 invalid
    {-1,-1,-1,-1,-1,-1,   0}, //62 invalid
    {-1,-1,-1,-1,-1,-1,   0}  //63 invalid
};

#ifdef USE_DIRECT3D
vector3 cubemap_direction_vector[6] =
{
vector3( 1, 0, 0),//RIGHT
vector3(-1, 0, 0),//LEFT
vector3( 0, 1, 0),//TOP
vector3( 0,-1, 0),//BOTTOM
vector3( 0, 0, 1),//FRONT
vector3( 0, 0,-1),//BACK
};

vector3 cubemap_up_vector[6] =
{
vector3( 0, 1, 0),//RIGHT
vector3( 0, 1, 0),//LEFT
vector3( 0, 0,-1),//TOP
vector3( 0, 0, 1),//BOTTOM
vector3( 0, 1, 0),//FRONT
vector3( 0, 1, 0),//BACK
};
#endif//USE_DIRECT3D

//#ifdef USE_OPENGL
vector3f cubemap_direction_vector[6] =
{
vector3f( 1, 0, 0),//RIGHT
vector3f(-1, 0, 0),//LEFT
vector3f( 0, 1, 0),//TOP
vector3f( 0,-1, 0),//BOTTOM
vector3f( 0, 0, 1),//FRONT
vector3f( 0, 0,-1),//BACK
};

vector3f cubemap_up_vector[6] =
{
vector3f( 0, -1, 0),//RIGHT
vector3f( 0, -1, 0),//LEFT
vector3f( 0, 0, 1),//TOP
vector3f( 0, 0,-1),//BOTTOM
vector3f( 0, -1, 0),//FRONT
vector3f( 0, -1, 0),//BACK
};
//#endif//USE_OPENGL

int Camera::window_width = 0;
int Camera::window_height = 0;

void Camera::setApplicationWindowSize(int width, int height)
{
    window_width = width;
    window_height = height;
}

Camera::Camera()
{ 
	eye_point.set(0,0,5);
	center_point.set(0,0,0);
	up_vector.set(0,1,0);

	//assert(window_width != 0 && window_height != 0 && "Camera::ctor: Creating a camera before window is created, imposible to determine window size to store in viewport");
	viewport[0] = 0.0f;
	viewport[1] = 0.0f;
	viewport[2] = window_width;
	viewport[3] = window_height;

	perspective_fov = 45.0f;
	if( window_height == 0 )
		perspective_aspect = 1.0f;
	else
		perspective_aspect = window_width / (float)window_height; //1.33f; 
	z_near = 1.0f;
	z_far = 1000.0f;
	ortho_left = -1;
	ortho_right = 1;
	ortho_bottom = -1;
	ortho_top = 1;

	projection_type = PERSPECTIVE;
	computeViewMatrix();
	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

void Camera::setProjectionType(ProjectionType p)
{
	projection_type = p;
	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

Camera::ProjectionType Camera::getProjectionType() const
{
	return projection_type;
}

void Camera::updateUpVector()
{
	vector3f temp = CrossProduct(eye_point - center_point,up_vector);
	up_vector = CrossProduct(temp,eye_point - center_point);
	up_vector.normalize();
}

void Camera::lookAt(const vector3f& eye,const vector3f& center,const vector3f& up)
{
	eye_point = eye;
	center_point = center;
	
  // This up_vector is not orthonormal to the front and left vectors. It's given
  // just to specify the roll of the camera in this method ( and used in the computeViewMatrix )
	up_vector = up;
	//updateUpVector();

	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::lookAt(const vector3f& eye,const quaternion& orientation)
{
	eye_point = eye;
	orientation.conjugate().toMatrix(view_matrix);
	matrix44 trans;
	trans.identity();
	trans.setTranslation(-eye);
	view_matrix = trans*view_matrix;

	// compute up vector and center point
	gti::vector3f zv;
	view_matrix.copyRow(1,up_vector);
	view_matrix.copyRow(2,zv);
	center_point = eye - zv;

	//computeLookAtVectorsFromViewMatrix(eye_point,center_point,up_vector);
	computeViewProjectionMatrix();
}

void Camera::lookAt(const vector3f& eye, CubemapFace face)
{
	//assert(face <= 6);
	//face = POSX;
	lookAt(eye, eye + cubemap_direction_vector[face], cubemap_up_vector[face]);
	setFov(90.0f);
	setAspect(1.0);
}

const vector3f& Camera::getEyePoint() const
{
	return eye_point;
}

const vector3f& Camera::getCenterPoint() const
{
	return center_point;
}

const vector3f& Camera::getUpVector() const
{
	return up_vector;
}

vector3f Camera::getForwardVector() const
{
	vector3f v;
	view_matrix.copyRow(2,v);
	return -v;
}

float Camera::getNearDistance() const
{
	return z_near;
}

float Camera::getFarDistance() const
{
	return z_far;
}

const matrix44& Camera::getViewMatrix() const
{
	return view_matrix;
}

const matrix44& Camera::getProjectionMatrix() const
{
	return projection_matrix;
}

const matrix44& Camera::getViewProjectionMatrix() const
{
	return view_projection_matrix;
}

void Camera::setEyePoint(const vector3f& newEye)
{
	eye_point = newEye;
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::setCenterPoint(const vector3f& newCenter)
{
	center_point = newCenter;
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::setNearDistance(float distance)
{
	z_near = distance;
	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

void Camera::setFarDistance(float distance)
{
	z_far = distance;
	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

void Camera::setAspect(float aspect)
{
	perspective_aspect = aspect;
	if (projection_type == PERSPECTIVE)
	{
		computeProjectionMatrix();
		computeViewProjectionMatrix();
	}
}

float Camera::getAspect() const
{
	return perspective_aspect;
}

void Camera::setFov(float fov)
{
	perspective_fov = fov;
	if (projection_type == PERSPECTIVE)
	{
		computeProjectionMatrix();
		computeViewProjectionMatrix();
	}
}

float Camera::getFov() const
{
	return perspective_fov;
}

void Camera::setClippingPlanes(float left, float right, float bottom, float top)
{
	ortho_left = left;
	ortho_right = right;
	ortho_bottom = bottom;
	ortho_top = top;

	if (projection_type == ORTHOGRAPHIC)
	{
		computeOrthographicMatrix();
		computeViewProjectionMatrix();
	}
	else if ( projection_type == FRUSTUM )
	{
		computeFrustumMatrix();
		computeViewProjectionMatrix();
	}
}

void Camera::getClippingPlanes(float& left, float& right, float& bottom, float& top) const
{
	left = ortho_left;
	right = ortho_right;
	bottom = ortho_bottom;
	top = ortho_top;
}


void Camera::perspective()
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
//	gluPerspective(perspective_fov,perspective_aspect,z_near,z_far);
	glMultMatrixf(projection_matrix);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
//	gluLookAt(
//		eye_point.x, eye_point.y,eye_point.z,
//		center_point.x,center_point.y,center_point.z,
//		upVector.x,upVector.y,upVector.z);
	glMultMatrixf(view_matrix);
}

void Camera::ortho()
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMultMatrixf(projection_matrix);
//	glOrtho(ortho_left,ortho_right,ortho_bottom,ortho_top,z_near,z_far);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
//	gluLookAt(
//		eye_point.x, eye_point.y,eye_point.z,
//		center_point.x,center_point.y,center_point.z,
//		upVector.x,upVector.y,upVector.z);
	glMultMatrixf(view_matrix);
//	getMVPMatrices();
//	computeFrustumPlanes();
}

void Camera::set()
{
	if (viewport[2] <= 0 &&  viewport[3] <= 0)
	{
		//gti::error("Error in Cammera::set(): Camera viewport can't be empty.\n");
		return;
	}
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMultMatrixf(projection_matrix);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMultMatrixf(view_matrix);

	if (viewport[2] == 0 &&  viewport[3] == 0)
	{
		//gti::error("Error in Cammera::set(): Camera viewport can't be empty.\n");
		return;
	}
	glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
	if(glGetError() != GL_NO_ERROR)
	{
		//gti::error("OpenGL error in Cammera::set(): %s\n",gluErrorString(0)); 
		return;
	}
}

void Camera::compute( )
{
	computeViewProjectionMatrix( );
	computeFrustumMatrix( );
}

void Camera::setCameraPerspective(float fov, float aspect, float neard, float fard)
{
	setProjectionType( Camera::PERSPECTIVE );
	setFov(fov);
	setAspect(aspect);
	setNearDistance( neard);
	setFarDistance( fard);
}

void Camera::setCameraOrthogonal(float left, float right, float bottom, float top, float neard, float fard)
{
	setProjectionType( Camera::ORTHOGRAPHIC );
	setClippingPlanes(left, right, bottom, top);
	setNearDistance( neard );
	setFarDistance( fard );
}

void Camera::computeProjectionMatrix()
{
	if (projection_type == PERSPECTIVE)
		computePerspectiveMatrix();
	else if (projection_type == ORTHOGRAPHIC)
		computeOrthographicMatrix();
	else
		computeFrustumMatrix();
}

void Camera::computeViewProjectionMatrix()
{
	view_projection_matrix = Camera::view_matrix * Camera::projection_matrix;
	computeFrustumPlanes();
}

void Camera::setCameraMatrices(const matrix44& view, const matrix44& projection)
{
	view_matrix = view;
	projection_matrix = projection;
	computeViewProjectionMatrix();
}

void Camera::forward(float meters)
{
	vector3f dir;
	// row 2 contains the inverse direction of projection
	view_matrix.copyRow(2,dir);
	//dir = center_point-eye_point;dir.normalize();
	meters = -meters;
	const vector3f& offset = dir*meters;
	eye_point += offset;
	center_point += offset;
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::strafeRight(float meters)
{
	vector3f xDirection;
	// row 1 contains the perpendicular direction to the direction of projection
	view_matrix.copyRow(0,xDirection);
	const vector3f& offset = xDirection*meters;
	eye_point += offset;
	center_point += offset;
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::strafeUp(float meters)
{
	vector3f yDirection;
	// row 1 contains the perpendicular direction to the direction of projection
	view_matrix.copyRow(1,yDirection);
	const vector3f& offset = yDirection*meters;
	eye_point += offset;
	center_point += offset;
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::translate(const vector3f& t)
{
	eye_point += t;
	center_point += t;
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::translateEye(const vector3f& t)
{
	eye_point += t;
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::translateCenter(const vector3f& t)
{
	center_point += t;
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::turnCenter(const vector3f& v, float radians)
{
	quaternion quat(v,radians);
	matrix33 rot;quat.toMatrix(rot);

	vector3f lookat_vector = center_point-eye_point;
	float distanceToTarget = lookat_vector.length();
	lookat_vector *= 1.0f/distanceToTarget;
	lookat_vector = rot * lookat_vector;
	
	up_vector = rot * up_vector;
	eye_point = center_point - lookat_vector * distanceToTarget;

	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::yawCenter(float radians)
{
	turnCenter(up_vector,radians);
}

void Camera::pitchCenter(float radians)
{
	vector3f rightVector;
	rightVector.set(view_matrix._11, view_matrix._21, view_matrix._31);
	turnCenter(rightVector,radians);
}

void Camera::rollCenter(float radians)
{
	vector3f lookat;
	lookat.set(view_matrix._13, view_matrix._23, view_matrix._33);
	turnCenter(-lookat,radians);
}

void Camera::turn(const vector3f& v, float radians)
{
	quaternion q;
	q.setAxisAngle(v,radians);
	matrix33 rot;q.toMatrix(rot);

	vector3f lookat_vector = eye_point - center_point;
	float distanceToTarget = lookat_vector.length();
	lookat_vector *= 1.0f/distanceToTarget;
	lookat_vector = rot * lookat_vector;
	
	up_vector = rot * up_vector;
	center_point = eye_point - lookat_vector * distanceToTarget;

	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::yaw(float radians)
{
//	if (fabsf(DotProduct(vector3f(0,1,0),up_vector)) < 0.999f)
//	if (up_vector.y < 0)
//		radians = -radians;
//	turn(vector3f(0,1,0),radians);
//	else
	turn(up_vector,radians);
//	else turn(vector3f(1,0,0),radians);
}

void Camera::pitch(float radians)
{
	vector3f rightVector;
	rightVector.set(view_matrix._11, view_matrix._21, view_matrix._31);
	turn(rightVector,radians);
}

void Camera::roll(float radians)
{
	vector3f lookat;
	lookat.set(view_matrix._13, view_matrix._23, view_matrix._33);
	turn(-lookat,radians);
}

const Plane& Camera::left()   const {return planes[AABB::LEFT];}
const Plane& Camera::right()  const {return planes[AABB::RIGHT];}
const Plane& Camera::top()    const {return planes[AABB::TOP];}
const Plane& Camera::bottom() const {return planes[AABB::BOTTOM];}
const Plane& Camera::front()  const {return planes[AABB::FRONT];}
const Plane& Camera::back()   const {return planes[AABB::BACK];}
const Plane* Camera::getPlanes() const { return &planes[0];}

void Camera::computeFrustumPlanes()
{
	extractFrustumPlanes(view_projection_matrix,planes);
}

CullFlag Camera::inside(const vector3f& v) const
{
	if (planes[AABB::LEFT].getDistance(v) < 0.0f) return OUTSIDE;
	if (planes[AABB::RIGHT].getDistance(v) < 0.0f) return OUTSIDE;
	if (planes[AABB::FRONT].getDistance(v) < 0.0f) return OUTSIDE;
	if (planes[AABB::BACK].getDistance(v) < 0.0f) return OUTSIDE;
	if (planes[AABB::TOP].getDistance(v) < 0.0f) return OUTSIDE;
	if (planes[AABB::BOTTOM].getDistance(v) < 0.0f) return OUTSIDE;
	
	return INSIDE;
}


CullFlag Camera::overlap(const AABB& box) const
{
	CullFlag flag;
	int o = 0;

	flag = planes[AABB::LEFT].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::RIGHT].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::FRONT].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::BACK].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::TOP].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::BOTTOM].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;

	if (o==0) return INSIDE;
	else return OVERLAP;
}

void Camera::setViewport(float left, float bottom, float width, float height)
{
	viewport[0] = left;
	viewport[1] = bottom;
	viewport[2] = width;
	viewport[3] = height;
}

void Camera::setViewportNormalized(float left, float bottom, float width, float height)
{
	viewport[0] = left * float(window_width);
	viewport[1] = bottom * float(window_height);
	viewport[2] = width * float(window_width);
	viewport[3] = height * float(window_height);
}

void Camera::getViewport(float* v)
{
	v[0] = viewport[0];
	v[1] = viewport[1];
	v[2] = viewport[2];
	v[3] = viewport[3];
}

void Camera::getViewportNormalized(float* v)
{
	v[0] = viewport[0] / (float)window_width;
	v[1] = viewport[1] / (float)window_height;
	v[2] = viewport[2] / (float)window_width;
	v[3] = viewport[3] / (float)window_height;
}

void Camera::project(const vector3f& point, float& x, float& y, float& z) const
{
	vector3f temp;
	view_projection_matrix.project(point,temp);
	// coordinates are in range [-1, 1]
	x = viewport[0] + viewport[2] * (temp[0] + 1) / 2;
	y = viewport[1] + viewport[3] * (temp[1] + 1) / 2;
	z = (temp[2]+1) / 2;
}

void Camera::unproject(float x, float y, float z, vector3f& point) const
{
	vector3f temp;
	matrix44 minv;
	bool ret = view_projection_matrix.inverse(minv);
	if(ret == false)
	{
		//gti::error("Error in Camera::unproject() inverting the camera projection matrix.\n");
		return;
	}
	
	//x = viewport[0] + viewport[2] * (temp[0] + 1) / 2;
	//y = viewport[1] + viewport[3] * (temp[1] + 1) / 2;

	// transform from screen space to normalized device coordinates (ndc)
	vector4f v4;
	v4.x = ((x-viewport[0])*2.0f)/viewport[2] - 1.0f;
	v4.y = ((y-viewport[1])*2.0f)/viewport[3] - 1.0f;
	v4.z = (z*2.0f) - 1.0f;
	v4.w = 1.0f;

	vector4f p4;
	p4 = minv * v4;
	float invw = 1.0f/p4.w;

	point.x = p4.x * invw;
	point.y = p4.y * invw;
	point.z = p4.z * invw;

}


float Camera::computeProjectedArea(const AABB& box) const
{
// Dieter Schmalstieg and Robert F. Tobler. 
// Fast projected area computation for three-dimensional bounding boxes. 
// Journal of Graphics Tools, 4(2):37-43, 1999 
	const vector3f& min = box.center - box.halfsize;
	const vector3f& max = box.center + box.halfsize;

    //compute 6-bit code to classify eye with respect to the 6 defining planes
    //of the bbox

    int pos = ((eye_point[0] < min[0]) ?  1 : 0)   // 1 = left
            + ((eye_point[0] > max[0]) ?  2 : 0)   // 2 = right
            + ((eye_point[1] < min[1]) ?  4 : 0)   // 4 = bottom
            + ((eye_point[1] > max[1]) ?  8 : 0)   // 8 = top
            + ((eye_point[2] < min[2]) ? 16 : 0)   // 16 = front
            + ((eye_point[2] > max[2]) ? 32 : 0);  // 32 = back

    int num = indexlist[pos][6]; //look up number of vertices in outline
    if (!num) return -1.0;       //zero indicates invalid case, return -1

    vector3f vertexBox[8];
	float dst[8][2];

    //generate 8 corners of the bbox
    vertexBox[0].set(min[0],min[1],min[2]); //     7+------+6
    vertexBox[1].set(max[0],min[1],min[2]); //     /|     /|
    vertexBox[2].set(max[0],max[1],min[2]); //    / |    / |
    vertexBox[3].set(min[0],max[1],min[2]); //   / 4+---/--+5
    vertexBox[4].set(min[0],min[1],max[2]); // 3+------+2 /    y   z
    vertexBox[5].set(max[0],min[1],max[2]); //  | /    | /     |  /
    vertexBox[6].set(max[0],max[1],max[2]); //  |/     |/      |/
    vertexBox[7].set(min[0],max[1],max[2]); // 0+------+1      *---x

    float area; int i;
    for(i=0; i<num; i++) //transform all outline corners into 2D screen space
	{
		float z;//unused
        project(vertexBox[indexlist[pos][i]],dst[i][0],dst[i][1],z);//project

//#ifdef _DEBUG
//		glColor3f(0,0,1);
//		glPointSize(10);
//		glBegin(GL_POINTS);
//		glVertex3fv(vertexBox[indexlist[pos][i]]);
//		glEnd();
//#endif
		// TODO: viewport[0] and viewport[1] are always 0
		if (dst[i][0] < viewport[0]) dst[i][0] = viewport[0];
		if (dst[i][1] < viewport[1]) dst[i][1] = viewport[1];
		if (dst[i][0] > viewport[2]) dst[i][0] = viewport[2];
		if (dst[i][1] > viewport[3]) dst[i][1] = viewport[3];
	}

    area = (dst[num-1][0] - dst[0][0]) * (dst[num-1][1] + dst[0][1]);
    for (i=0; i<num-1; i++)
        area += (dst[i][0] - dst[i+1][0]) * (dst[i][1] + dst[i+1][1]);

    return area * 0.5f; //return computed value corrected by 0.5
}

void Camera::renderFrustum() const
{
	glPushMatrix();

//	glColor3f(1,1,1);

	// draw axis
/*
	glBegin(GL_LINES);
	glColor3f(1,0,0);
	glVertex3fv(eye_point);
	glVertex3fv(eye_point + vector3f(view._11,view._21,view._31) );
	glColor3f(0,1,0);
	glVertex3fv(eye_point);
	glVertex3fv(eye_point+vector3f(view._12,view._22,view._32));
	glColor3f(0,0,1);
	glVertex3fv(eye_point);
	glVertex3fv(eye_point+vector3f(view._13,view._23,view._33));
	glEnd();
*/
	matrix44 inverse;
	view_projection_matrix.inverse(inverse);
	glMultMatrixf(inverse);

	AABB aabb;
	aabb.center.set(0,0,0);
	aabb.halfsize.set(1,1,1);

	gti::matrix44 id;
	id.identity();
	aabb.renderWire(id);

//	glColor3f(1,1,1);

	glPopMatrix();
}

void Camera::computeViewMatrix()
{
	view_matrix.cameraLookAt(eye_point,center_point,up_vector);
}

void Camera::computeLookAtVectorsFromViewMatrix(vector3f& eye, vector3f& center, vector3f& up) const
{
	matrix44 m;
	vector3f s;

	m = view_matrix;
	m.setNoTranslation();
	m = view_matrix * m;
	// the last column contains the negated eye_point
	m.copyTranslation(eye);
	eye = -eye;
	// view row 2 got the inverse direction of projection (-z axis)
	view_matrix.copyRow(2,center);
	center = -center;
	// view row 1 got the up vector
	view_matrix.copyRow(1,up);
	// a possible center point is simply eye_point + direction of projection
	center = eye + center;
}

void Camera::computePerspectiveMatrix()
{
	projection_matrix.setProjection(perspective_fov, perspective_aspect, z_near, z_far);
}

void Camera::computeFrustumMatrix()
{
//	glFrustum call (column order)
//
// |    2n/(r-l)       0         (r+l)/(r-l)     0       |
// |      0          2n/(t-b)    (t+b)/(t-b)     0       |
// |      0            0         (n+f)/(n-f) (2nf)/(n-f) |
// |      0            0              -1         0       |

	float angle = deg2rad(getFov());
    float aspect = getAspect();
    
	float nearval = getNearDistance();
//	float farval = getFarDistance();
    
	//float nearToFar = farval - nearval;

	float top = nearval * float(tanf(angle/2.0f));
	float bottom = -top;
	float left = bottom * aspect;
	float right = -left;

	float zs = z_near-z_far;
	if (fabsf(zs) < 0.00001f)
	{
		//gti::error("Error in Camera::computeFrustumMatrix(): compute projection matrix, division by zero.\n");
		projection_matrix.clear();
	}
	zs = 1.0f/zs;	
	
	projection_matrix.clear();
	projection_matrix._11 = (2*z_near)/(right - left );
	projection_matrix._22 = (2*z_near)/(top - bottom );
	projection_matrix._31 = (right + left) / (right - left );
	projection_matrix._32 = (top + bottom )/(top - bottom );
	projection_matrix._33 = (z_far+z_near)*zs;
	projection_matrix._34 = -1;
	projection_matrix._43 = (2*z_far*z_near)*zs;

	// linearize depth
	//projection_matrix._33 /= z_far;
	//projection_matrix._43 /= z_far;
}

//normalized viewport 
void Camera::updateFrustumMatrixFromViewport(float* client_viewport, bool normalized)
{
	//*
	float angle = deg2rad(getFov());
    float aspect = getAspect();
    
	float nearval = getNearDistance();
//	float farval = getFarDistance();
    
//	float nearToFar = farval - nearval;

	float top = nearval * tanf(angle/2.0f);
	float bottom = -top;
	float left = bottom * aspect;
	float right = -left;

	float iwidth = ( normalized ? 1 : 1 / (float)window_width);
	float iheight = ( normalized ? 1 : 1 / (float)window_height);

	float totalWidth = fabs(right-left);
	float totalHeight = fabs(top-bottom);
//	float fragmentWidth = totalWidth * (viewport[2] / (float)window_width);
//	float fragmentHeight = totalHeight * (viewport[3] / (float)window_height);
//	float pixelWidth = totalWidth / (float)window_width;
//	float pixelHeight = totalHeight / (float)window_height;

	//normalized viewport
	/*
	left += client_viewport[0] * iwidth * totalWidth ;
	right -= (1 - (client_viewport[2] - client_viewport[0]) * iwidth) * totalWidth;
	bottom += client_viewport[1] * iheight * totalHeight;
	top -= (1 - (client_viewport[3] - client_viewport[1]) * iheight) * totalHeight;
	*/

	left += client_viewport[0] * iwidth * totalWidth ;
	right -= (1 - client_viewport[2] * iwidth - client_viewport[0] * iwidth) * totalWidth;
	bottom += client_viewport[1] * iheight * totalHeight;
	top -= (1 - client_viewport[3] * iheight - client_viewport[1] * iheight) * totalHeight;

	float zs = z_near-z_far;
	if (fabsf(zs) < 0.00001f)
	{
		//gti::error("Error in Camera::updateFrustumMatrixFromViewport(): compute projection matrix, division by zero.\n");
		projection_matrix.clear();
	}

	zs = 1.0f/zs;	

	projection_matrix.clear();
	projection_matrix._11 = (2*z_near)/(right - left );
	projection_matrix._22 = (2*z_near)/(top - bottom );
	projection_matrix._31 = (right + left) / (right - left );
	projection_matrix._32 = (top + bottom )/(top - bottom );
	projection_matrix._33 = (z_far+z_near)*zs;
	projection_matrix._34 = -1;
	projection_matrix._43 = (2*z_far*z_near)*zs;

	computeViewProjectionMatrix();
}

/*
void Camera::computeFrustumMatrix()
{
//	glFrustum call (column order)
//
// |    2n/(r-l)       0         (r+l)/(r-l)     0       |
// |      0          2n/(t-b)    (t+b)/(t-b)     0       |
// |      0            0         (n+f)/(n-f) (2nf)/(n-f) |
// |      0            0              -1         0       |

	float angle = deg2rad(getFov());
    float aspect = getAspect();
    
	float nearval = getNearDistance();
	float farval = getFarDistance();
    
	float nearToFar = farval - nearval;

	float top = nearval * float(tanf(angle/2.0f));
	float bottom = -top;
	float left = bottom * aspect;
	float right = -left;

	float totalWidth = fabs(right-left);
	float fragmentWidth = totalWidth/number_of_nodes[0];
	float pixelWidth = totalWidth/window_width;

	left+=(node_id[0])*fragmentWidth - pixelWidth*margin_pixels;
	right-=(number_of_nodes[0]-1-node_id[0])*fragmentWidth - pixelWidth*margin_pixels;

	float totalHeight = fabs(top-bottom);
	float fragmentHeight = totalHeight/number_of_nodes[1];
	float pixelHeight = totalHeight/window_height;

	bottom+=(node_id[1])*fragmentHeight - pixelHeight*margin_pixels;
	top-=(number_of_nodes[1]-1-node_id[1])*fragmentHeight - pixelHeight*margin_pixels;

	float zs = z_near-z_far;
	if (fabsf(zs) < 0.00001f)
	{
		assert(0 && "compute projection matrix, division by zero"); //division by zero
		projection.clear();
	}
	zs = 1.0f/zs;	
	
	projection.clear();
	projection._11 = (2*z_near)/(right - left );
	projection._22 = (2*z_near)/(top - bottom );
	projection._31 = (right + left) / (right - left );
	projection._32 = (top + bottom )/(top - bottom );
	projection._33 = (z_far+z_near)*zs;
	projection._34 = -1;
	projection._43 = (2*z_far*z_near)*zs;

}
*/
void Camera::computeOrthographicMatrix()
{
	projection_matrix.setOrthoProjection(ortho_left,ortho_right,ortho_bottom,ortho_top,z_near,z_far);
/*
// glOrtho call
//
// |     2/(r-l)       0            0       (r+l)/(r-l)  |
// |      0           2/(t-b)       0       (t+b)/(t-b)  |
// |      0            0         -2/(f-n)   (f+n)/(f-n)  |
// |      0            0            0            1       |
// 
	const float width = ortho_right - ortho_left;
	const float height = ortho_top - ortho_bottom;
	const float depth = z_far - z_near;
	vector3f translation;
	
	projection_matrix.clear();
	projection_matrix._11 = 2.0f / width;
	projection_matrix._22 = 2.0f / height;
	projection_matrix._33 = -2.0f / depth;

	translation.set(
		-(ortho_right + ortho_left)/width,
		-(ortho_top + ortho_bottom)/height,
		-(z_far + z_near)/depth
		);
	projection_matrix.setTranslation(translation);
	projection_matrix._44 = 1;
	*/
}

void Camera::zoom(const vector3f& sphereCenter, float radius)
{
	center_point = sphereCenter;
	zoom(radius*2);

	computeViewMatrix();
	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

void Camera::viewAll(const vector3f& sphereCenter, float radius)
{
	center_point = sphereCenter;
	vector3f dir = (center_point - eye_point);
	float len = dir.length();
	dir.normalize();
	len -= (radius*2 + z_near);

	// TODO: having in account the aperture size to fit the aabb
	//perspective_fov = RadToDeg( ( 2.0f * atan2f(radius,len)) );
	//float fov = DegToRad(perspective_fov)/2.0f;

	eye_point += dir*len;
	
	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::viewAll(const AABB& aabb)
{
	float radius;
	vector3f corner = aabb.center+aabb.halfsize;
	radius = (corner - aabb.center).length();

	viewAll(aabb.center,radius);
}


void Camera::shrink(const vector3f& sphereCenter, float radius)
{
	center_point = sphereCenter;
	ortho_left = sphereCenter.x - radius;
	ortho_right = sphereCenter.x + radius;
	ortho_bottom = sphereCenter.z - radius;
	ortho_top = sphereCenter.z + radius;

	if ( perspective_aspect < 1.0 ) // window taller than wide
	{ 
		float iaspect = 1.0f/perspective_aspect;
		ortho_bottom *= iaspect;
		ortho_top *= iaspect;
	}
	else
	{
		ortho_left *= perspective_aspect;
		ortho_right *= perspective_aspect;
	}
	
	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

void Camera::zoom(const AABB& aabb)
{
	float radius;
	vector3f corner = aabb.center+aabb.halfsize;
	radius = (corner - aabb.center).length();

	zoom(aabb.center,radius);
}

void Camera::shrink(const AABB& aabb)
{
	//float diam = aabb.halfsize.x*2 + aabb.halfsize.y*2 + aabb.halfsize.z*2;
	float radius;
	vector3f corner = aabb.center+aabb.halfsize;
	radius = (corner - aabb.center).length();

	shrink(aabb.center,radius);
}

void Camera::zoom(float diameter)
{
// The solution is in the viewing chapter of OpenGL Red book
// adjust the lens camera
	float len = (center_point - eye_point).length();
	perspective_fov = rad2deg( ( 2.0f * atan2f(diameter/2.0f,len)) );

	if (perspective_fov > 180.0f)
		perspective_fov = 180.0f;

	if (perspective_fov < 0.1f)
		perspective_fov = 0.1f;

	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

void Camera::shrink(float percent)
{
// the method adjust the vertical and horizontal clipping planes
// to move away or far from the center point
	percent += 1;
	ortho_left *= percent;
	ortho_right *= percent;
	ortho_bottom *= percent;
	ortho_top *= percent;

	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

void Camera::virtualSlide(float value)
{
	vector3f dir;
	//dir = (eye_point-center_point);dir.normalize();
	// row2 contains the inverse direction of projection
	view_matrix.copyRow(2,dir);
	eye_point += dir*value;
	center_point += dir*value;
	z_near += value;
	z_far += value;
	//perspective_fov -= 5;

	computeViewMatrix();
	computeProjectionMatrix();
	computeViewProjectionMatrix();
}

void Camera::extractFrustumPlanes(const matrix44& vp, Plane* planes)
{
	// This will extract the RIGHT side of the frustum
	planes[AABB::RIGHT].n.x = vp[ 3] - vp[ 0];
	planes[AABB::RIGHT].n.y = vp[ 7] - vp[ 4];
	planes[AABB::RIGHT].n.z = vp[11] - vp[ 8];
	planes[AABB::RIGHT].d = vp[15] - vp[12];

	// Normalize the RIGHT side
	planes[AABB::RIGHT].normalize();

	// This will extract the LEFT side of the frustum
	planes[AABB::LEFT].n.x = vp[ 3] + vp[ 0];
	planes[AABB::LEFT].n.y = vp[ 7] + vp[ 4];
	planes[AABB::LEFT].n.z = vp[11] + vp[ 8];
	planes[AABB::LEFT].d = vp[15] + vp[12];

	// Normalize the LEFT side
	planes[AABB::LEFT].normalize();

	// This will extract the BOTTOM side of the frustum
	planes[AABB::BOTTOM].n.x = vp[ 3] + vp[ 1];
	planes[AABB::BOTTOM].n.y = vp[ 7] + vp[ 5];
	planes[AABB::BOTTOM].n.z = vp[11] + vp[ 9];
	planes[AABB::BOTTOM].d = vp[15] + vp[13];

	// Normalize the BOTTOM side
	planes[AABB::BOTTOM].normalize();

	// This will extract the TOP side of the frustum
	planes[AABB::TOP].n.x = vp[ 3] - vp[ 1];
	planes[AABB::TOP].n.y = vp[ 7] - vp[ 5];
	planes[AABB::TOP].n.z = vp[11] - vp[ 9];
	planes[AABB::TOP].d = vp[15] - vp[13];

	// Normalize the TOP side
	planes[AABB::TOP].normalize();

	// This will extract the BACK side of the frustum
	planes[AABB::BACK].n.x = vp[ 3] - vp[ 2];
	planes[AABB::BACK].n.y = vp[ 7] - vp[ 6];
	planes[AABB::BACK].n.z = vp[11] - vp[10];
	planes[AABB::BACK].d = vp[15] - vp[14];

	// Normalize the BACK side
	planes[AABB::BACK].normalize();

	// This will extract the FRONT side of the frustum
	planes[AABB::FRONT].n.x = vp[ 3] + vp[ 2];
	planes[AABB::FRONT].n.y = vp[ 7] + vp[ 6];
	planes[AABB::FRONT].n.z = vp[11] + vp[10];
	planes[AABB::FRONT].d = vp[15] + vp[14];

	// Normalize the FRONT side
	planes[AABB::FRONT].normalize();
}

CullFlag Camera::overlap(const Plane* planes, const AABB& box)
{
	CullFlag flag;
	int o = 0;

	flag = planes[AABB::LEFT].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::RIGHT].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::FRONT].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::BACK].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::TOP].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;
	flag = planes[AABB::BOTTOM].overlap(box);
	if (flag == OUTSIDE) return OUTSIDE; o+= flag;

	if (o==0) return INSIDE;
	else return OVERLAP;
}

CullFlag Camera::overlap(const vector3f& center, float radius) const
{
	float dist;
	bool overlap = false;

	dist = planes[LEFT].getDistance(center);
	if( dist < -radius ) return OUTSIDE;
	else if(dist >= -radius && dist <= radius)	overlap = true;
	dist = planes[RIGHT].getDistance(center);
	if( dist < -radius ) return OUTSIDE;
	else if(dist >= -radius && dist <= radius)	overlap = true;
	dist = planes[FRONT].getDistance(center);
	if( dist < -radius ) return OUTSIDE;
	else if(dist >= -radius && dist <= radius)	overlap = true;
	dist = planes[BACK].getDistance(center);
	if( dist < -radius ) return OUTSIDE;
	else if(dist >= -radius && dist <= radius)	overlap = true;
	dist = planes[TOP].getDistance(center);
	if( dist < -radius ) return OUTSIDE;
	else if(dist >= -radius && dist <= radius)	overlap = true;
	dist = planes[BOTTOM].getDistance(center);
	if( dist < -radius ) return OUTSIDE;
	else if(dist >= -radius && dist <= radius)	overlap = true;
	if (overlap)
		return OVERLAP;
	return INSIDE;
}

//static method to get a matrix from a values
void Camera::computeViewMatrix(const vector3f& eye,const vector3f& center,const vector3f& up, matrix44& view)
{
// gluLookAt call (column order) and
// D3DXMatrixLookAtRH() (column order) generate the same matrix
// zaxis = normal(Eye - At)
// xaxis = normal(cross(Up, zaxis))
// yaxis = cross(zaxis, xaxis)

//  xaxis.x           yaxis.x           zaxis.x          0
//  xaxis.y           yaxis.y           zaxis.y          0
//  xaxis.z           yaxis.z           zaxis.z          0
// -dot(xaxis, eye)  -dot(yaxis, eye)  -dot(zaxis, eye)  1

// D3DXMatrixLookAtLH() (column order) generate a D3DXMatrixLookAtRH
// but zaxis = normal(At - Eye)

	vector3f xaxis,yaxis,zaxis, upv;
	upv = up;
	zaxis = eye - center;
	zaxis.normalize();
	float dotp = DotProduct(upv,zaxis);
	// angle between them is 180 
	if (fabsf(dotp) > 0.9999f) upv.set(1,0,0);
	xaxis = CrossProduct(upv, zaxis); xaxis.normalize();
	yaxis = CrossProduct(zaxis, xaxis);

    view._11 = xaxis.x;
	view._12 = yaxis.x; view._13 = zaxis.x; view._14 = 0;
	view._21 = xaxis.y; view._22 = yaxis.y; view._23 = zaxis.y; view._24 = 0;
	view._31 = xaxis.z; view._32 = yaxis.z; view._33 = zaxis.z; view._34 = 0;
	view._41 = -DotProduct(xaxis, eye);
	view._42 = -DotProduct(yaxis, eye);
	view._43 = -DotProduct(zaxis, eye);
	view._44 =  1;

#ifdef USE_DIRECT3D

	D3DXMATRIX tmp; D3DXMatrixLookAtLH(&tmp,
		(const D3DXVECTOR3*)&eye,
		(const D3DXVECTOR3*)&center,
		(const D3DXVECTOR3*)&upv); 
#endif

}

// -----------------------------------------------------------------
// (0.0, 0.0f) Upper - Left corner of the camera frustum
// (1.0, 1.0f) Lower - Right corner of the camera frustum
// (0.5, 0.5f) Center of the camera -> front
vector3f Camera::getRayDirection( float normalized_x_from_left_to_right, float normalized_y_from_top_to_bottom ) const
{
	float x = -( ( 2.0f * normalized_x_from_left_to_right ) - 1.0f );
	float y =  ( ( 2.0f * normalized_y_from_top_to_bottom ) - 1.0f );
	float d = 1.0f / tanf( getFov( ) * (float) M_PI / 180.0f * 0.5f );

  vector3f up, right, front;
	getViewMatrix( ).copyRow(0, right);
	getViewMatrix( ).copyRow(1, up);
	getViewMatrix( ).copyRow(2, front);

	vector3f ray_dir = front * d 
					         + right * x * getAspect( )
					         + up * y;
	ray_dir.normalize( );

  return -ray_dir;
}



/*
void* Camera::serialize(size_t& size)
{
    size = 0;
	static unsigned char data[255];

	memcpy(data[0], &projection_type, sizeof(ProjectionType) );
	size += sizeof(ProjectionType);

	memcpy(data[size], viewport, sizeof(float) * 4 );
	size += sizeof(float) * 4;

	memcpy(data[size], &eye_point, sizeof(vector3f) );
	size += sizeof(vector3f);
	memcpy(data[size], &center_point, sizeof(vector3f) );
	size += sizeof(vector3f);
	memcpy(data[size], &up_vector, sizeof(vector3f) );
	size += sizeof(vector3f);

	memcpy(data[size], &perspective_fov, sizeof(float) );
	size += sizeof(float);
	memcpy(data[size], &perspective_aspect, sizeof(float) );
	size += sizeof(float);
	memcpy(data[size], &ortho_left, sizeof(float) );
	size += sizeof(float);
	memcpy(data[size], &ortho_right, sizeof(float) );
	size += sizeof(float);
	memcpy(data[size], &ortho_bottom, sizeof(float) );
	size += sizeof(float);
	memcpy(data[size], &ortho_top, sizeof(float) );
	size += sizeof(float);
	memcpy(data[size], &z_near, sizeof(float) );
	size += sizeof(float);
	memcpy(data[size], &z_far, sizeof(float) );
	size += sizeof(float);

	assert(size < 256);

}

void Camera::unserialize(void* data, size_t size)
{
	//TODO
}
*/

//***********************

/* DEPRECATED
void Camera::setNumberOfNodesY(unsigned int y)
{
	number_of_nodes[1] = max(y,(unsigned int)1);
	node_id[1] = min(node_id[1],number_of_nodes[1]-1);

	if (projection_type == FRUSTUM)
	{
		computeFrustumMatrix();
		computeViewProjectionMatrix();
	}
}
void Camera::setNumberOfNodes( unsigned int x, unsigned int y )
{
	number_of_nodes[0] = max(x,(unsigned int)1);
	number_of_nodes[1] = max(y,(unsigned int)1);

	node_id[0] = min(node_id[0],number_of_nodes[0]-1);
	node_id[1] = min(node_id[1],number_of_nodes[1]-1);

	if (projection_type == FRUSTUM)
	{
		computeFrustumMatrix();
		computeViewProjectionMatrix();
	}
}

void Camera::setNodeIdY( unsigned int y )
{
	
	node_id[1] = min(y, number_of_nodes[1]-1);
	if ( projection_type == FRUSTUM )
	{
		computeFrustumMatrix();
		computeViewProjectionMatrix();
	}
}
void Camera::setNodeId( unsigned int x, unsigned int y)
{
	node_id[0] = min(x,number_of_nodes[0]-1);
	node_id[1] = min(y,number_of_nodes[1]-1);
	if (projection_type == FRUSTUM)
	{
		computeFrustumMatrix();
		computeViewProjectionMatrix();
	}
}

unsigned int Camera::getMarginPixels() const
{
	return margin_pixels;
}

void Camera::setMarginPixels(unsigned int pixels)
{
	margin_pixels = pixels;

	if (projection_type == FRUSTUM)
	{
		computeFrustumMatrix();
		computeViewProjectionMatrix();
	}
}

void Camera::getNumberOfNodes(unsigned int* nodes) const
{
	nodes[0]=number_of_nodes[0];
	nodes[1]=number_of_nodes[1];
}

void Camera::getNodeId(unsigned int *node) const
{
	node[0]=node_id[0];
	node[1]=node_id[1];
}

unsigned int Camera::getNumberOfNodesY() const
{
	return number_of_nodes[1];
}

unsigned int Camera::getNodeIdY() const
{
	return node_id[1];
}

unsigned int Camera::getNodeIdX() const
{
	return node_id[0];
}
*/


void Camera::turnPoint(const gti::vector3f& p, const gti::vector3f& v, float radians)
{
	gti::quaternion quat(v,radians);
	gti::matrix33 rot;quat.toMatrix(rot);
	
	gti::vector3f edir = (eye_point - p);
	gti::vector3f tdir = (center_point - p);
	eye_point = p + rot*edir;
	center_point = p + rot*tdir;
	up_vector = rot * up_vector;

	computeViewMatrix();
	computeViewProjectionMatrix();
}

void Camera::pitchPoint(const gti::vector3f& p, float radians)
{
	gti::vector3f rightVector;
	rightVector.set(view_matrix._11,view_matrix._21,view_matrix._31);
	turnPoint(p,rightVector,radians);
}

